package com.xpec.c4.game.datamanager;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.group.transportlayer.GroupMessage;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ConsistentGroupName;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.ServerProperties;

/**
 * 向 dc 存取資料用的基礎類別
 * <p>
 * 存取資料的流程範例 (如果要增加新的資料存取項目，就照這個流程一步一步實做出來就可以了)
 * <ul>
 * <li>透過 DataAgent 要求存取資料，DataAgent 會把要求用 group message 送給 dc
 * {@link com.xpec.c4.game.datamanager.agent.PlayerDataAgent#updatePlayerDataToDC(com.ms.player.Player, String, Object...)
 * link}
 * <li>dc 收到存取資料的要求
 * {@link com.xpec.c4.service.module.datacenter.player.PlayerDataProcess#updatePlayerInfo(DataRequestInfo)
 * link}
 * <li>轉給對應的 DataManager 處理
 * {@link com.xpec.c4.service.module.datacenter.player.PlayerDataManager#updateData(BaseDataInfo, String, Object...)
 * link}
 * <li>DataManager 用異步處理的 task 來執行要求
 * {@link com.xpec.c4.service.module.datacenter.player.UpdatePlayerDataAsync#asyncExecute()
 * link}
 * <li>異步處理 task 呼叫 DataManager 執行要求
 * {@link com.xpec.c4.game.character.PlayerManager#updatePlayerToDB(com.ms.player.Player)
 * link}
 * <li>DataManager 透過 xxxDAO 對資料庫進行存取
 * {@link com.xpec.c4.db.dao.PlayerDAO#saveOrUpdatePlayer(com.ms.player.Player)
 * link}
 * <li>異步處理 task 執行完成後呼叫回 DataManager 的 callback
 * {@link com.xpec.c4.service.module.datacenter.player.PlayerDataManager#updateDataCallback(int, BaseDataInfo, String, boolean)
 * link}
 * <li>callback 將執行結果傳回發出要求的 module，再呼叫一開始指定的 callback method
 * {@link com.xpec.c4.game.zone.PlayerDataAgentZone#changeNameUpdatePlayerDataToDCCallback(BaseDataInfo, Object...)
 * link}
 * <li>接回原本的流程，做取到資料後的處理
 * <li>結束
 * <ul>
 * 
 * @author yangbingyan
 */
public class DataManager<V> implements DataProcessCallback{	
	/** log4j object */
	private static final Log log = LogFactory.getLog(DataManager.class);
	
	/**
	 * 
	 */
	public final static int ERROR_HANDLE = -1;
	
	
	
	/**
	 * 這裡的map不可以在不同線程裡處理 
	 */
	
	/**
	 * key 與 數據<br>
	 * key 强迫使用GUID
	 */
	protected Map<GUID, V> dataMap = Collections.synchronizedMap(new HashMap<GUID, V>(2000));
	
	public int getSize()
	{
		return dataMap.size();
	}
	
	/**
	 * key 與 handle對應表
	 */
	private Map<GUID, Integer> handleMap = new HashMap<GUID, Integer>(2000);
	
	/**
	 * 被哪個module持有
	 */
	private BasicServiceModule module;
	
	/**
	 * 構造
	 * @param module 需指定是被哪一个module拥有
	 */
	public DataManager(BasicServiceModule module){
		this.module = module;
	}

	/**
	 * 
	 * @return
	 */
	public BasicServiceModule getModule() {
		return module;
	}
	
	/**
	 * 
	 * @return
	 */
	public String getModuleID(){
		if(module != null){
			return module.getModuleName();
		}
		return null;
	}

	// 所需要的存取權限
	// private int modifyType;
	
	/**
	 * 
	 * @param key
	 * @return
	 */
	protected Integer getDataHandleByKey(GUID key){
		return handleMap.get(key);
	}
	
	/**
	 * 获取数据
	 * @param key
	 * @return
	 */
	public V getData(GUID key){
		return dataMap.get(key);
	}
	
	/**
	 * 删除数据
	 * @param key
	 * @return
	 */
	public V removeData(GUID key){
		handleMap.remove(key);
		return dataMap.remove(key);
	}

	/**
	 * 添加数据
	 * @param key
	 * @param handle
	 * @param data
	 */
	public V putData(GUID key, Integer handle, V data){
		
//		if(dataMap.containsKey(key)){
//			return;
//		}
		
		V tmpValue = dataMap.put(key, data);		
		changeHandle(key, handle);
		return tmpValue;
	}
	
	/**
	 * 通過key改變handle
	 * @param key
	 * @param handle
	 * @return
	 */
	protected Integer changeHandle(GUID key, Integer handle){		
		return handleMap.put(key, handle);
	}

	/**
	 * 對DataCenter送出資料處理的請求<br>
	 * 
	 * 需要送到DataCenter
	 * 
	 * @param dataProcessCallback
	 *            預處理 callback 所在 class，沒有需要預處理的 dc 要求就給 null
	 * @param classOfPreProcMethod
	 *            預處理 callback，沒有需要預處理的 dc 要求就給 null
	 * @param preProcMethod
	 * @param classOfMethod
	 * @param responseMethod
	 * @param data
	 * @param userData
	 * @return
	 */
	public boolean dataRequest(DataProcessCallback dataProcessCallback, Object classOfPreProcMethod,
			Method preProcMethod, Object classOfMethod, Method responseMethod, BaseDataInfo data,
			Object... userData) {

		if (dataProcessCallback != null || userData != null
				|| responseMethod != null) {
			
			// UserData會存在本地端，待callback被呼叫時會與返回的資料一起傳給callback
			Long sn = module.addDataProcessCallbackAndUserData(classOfPreProcMethod, preProcMethod, classOfMethod,
					responseMethod, dataProcessCallback, userData);

			if (sn != null) {
				data.setSnCallback(sn.longValue());
			}
		}

		DataRequestInfo msgData = new DataRequestInfo();		
		msgData.setDataInfo(data);
		msgData.setModuleID(this.module.getModuleName());
		
		String destModuleName = null;
		if (data.getKey() != null) {
			destModuleName = ConsistentGroupName.lookUp(ModuleName.GROUP_DATA_CENTER, data.getKey());
			if (!GameUtility.isHaveString(destModuleName)) {
				if (log.isErrorEnabled()) {
					GameUtility.logErrorException(log, "get module name fail.");
				}
				return false;
			}
		} else {
			String firstModuleName = ServerProperties.getFirstModuleName(ModuleName.DATA_CENTER);
			if (!GameUtility.isHaveString(firstModuleName)) {
				if (log.isErrorEnabled()) {
					GameUtility.logErrorException(log, "get first dc module name fail.");
				}
				return false;
			}

			destModuleName = firstModuleName;
		}

		if (log.isInfoEnabled()) {
			log.info("send data request to " + destModuleName + ", key=" + data.getKey());
		}

		GroupMessage msg = new GroupMessage();
		msg.setMessageType(data.getMessageType());
		msg.setMessageContent(msgData);
		msg.setDestModuleID(destModuleName);
		MessageAction.sendMessage(msg);
		
		return true;
	}

	/**
	 * DataCenter回應數據(主動、被動) 
	 * @param data
	 * @param moduleID
	 * @return
	 */
	public boolean dataResponse(BaseDataInfo data, String moduleID){
	
		DataRequestInfo msgData = new DataRequestInfo();
		
		msgData.setDataInfo(data);
		msgData.setModuleID(this.module.getModuleName());

		GroupMessage msg = new GroupMessage();
		msg.setMessageType(MessageTypes.S_DATA_RESPONSE);
		msg.setMessageContent(msgData);
		msg.setDestModuleID(moduleID);
		MessageAction.sendMessage(msg);
		
		return true;
	}

	/**
	 * 通過名稱獲取Method by Class<br>
	 * 只能取得自己的method<br>
	 * @param methodName
	 * @return
	 */
	protected Method getCallbackMethod(Object target ,String methodName){
		
		if(methodName == null || methodName.length() < 1){
			return null;
		}
		
		Method mth = null;
		try 
		{
			if(target instanceof java.lang.Class)
			{
				mth = ((Class)target).getDeclaredMethod(methodName, new Class[] { BaseDataInfo.class, Object[].class });
				mth.setAccessible(true);
			}
			else
			{
				mth =target.getClass().getMethod(methodName,
						new Class[] { BaseDataInfo.class, Object[].class });
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return mth;
	}
	

	// 索取資料還是得透過message向別的module要, 可是消息回來時會在module...<br>
	// 要怎麼方便的把資料導回原本的DataManager?
	
	@Override
	public boolean dataProcessCallback(Object classOfMethod, Method callbackMethod, BaseDataInfo obj,
			Object... userObject) {

		// 用invoke好還是用processID直接switch好 ?<br>
		
		// switch 貌似比invoke快, 以後慢的話再改改吧<br>

		if (classOfMethod != null && callbackMethod != null) {

			try {
				callbackMethod.invoke(classOfMethod, new Object[] { obj, userObject });
				return true;
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}

		return false;
	}
	
	/**
	 * 
	 * @return
	 */
	public boolean shutdown(){
		return true;
	}
}
